using System;
using FamilyTreeProject.GEDCOM.Common;
using System.Text;

namespace FamilyTreeProject.GEDCOM.Records
{

    public class GEDCOMRecord
    {

        #region Fields

        private GEDCOMRecordList childRecords;
        private string data;
        private string id;
        private int level;
        private string tag;
        private string xRefId;

        #endregion

        #region Constructors

        /// <summary>
        /// Constructs a GEDCOMRecord object
        /// </summary>
        internal GEDCOMRecord()
        {
        }

        /// <summary>
        /// Constructs a GEDCOMRecord object
        /// </summary>
        /// <param name="record">The record of text that represents a GEDCOMRecord</param>
        internal GEDCOMRecord(string record)
        {
            Parse(record);
            this.childRecords = new GEDCOMRecordList();
        }

        /// <summary>
        /// Constructs a GEDCOMRecord object
        /// </summary>
        /// <param name="level">The level (or depth) of the GEDCOM Record</param>
        /// <param name="id">the id of the record</param>
        /// <param name="xRefId">An optional XrefId reference</param>
        /// <param name="tag">The tag name of the GEDCOM Record</param>
        /// <param name="data">The data part of the GEDCOM Record</param>
        internal GEDCOMRecord(int level, string id, string xRefId, string tag, string data)
        {
            this.level = level;
            this.id = id;
            this.xRefId = xRefId;
            this.tag = tag;
            this.data = data;
            this.childRecords = new GEDCOMRecordList();
        }

        /// <summary>
        /// Constructs a GEDCOMRecord object
        /// </summary>
        /// <remarks>This constructor is primarily to allow sublasses of GEDCOMRecord
        /// to be constructed from the base class</remarks>
        /// <param name="record">A GEDCOM Record</param>
        internal GEDCOMRecord(GEDCOMRecord record)
        {
            this.level = record.Level;
            this.id = record.Id;
            this.xRefId = record.XRefId;
            this.tag = record.Tag;
            this.data = record.Data;
            this.childRecords = record.ChildRecords;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the Child GEDCOM Records for this GEDCOM Record
        /// </summary>
        public GEDCOMRecordList ChildRecords
        {
            get { return childRecords; }
        }

        /// <summary>
        /// Gets or sets the Data part of the GEDCOMRecord
        /// </summary>
        public string Data
        {
            get { return this.data; }
            set { this.data = value; }
        }

        /// <summary>
        /// Gets whether the Record has any Child Records
        /// </summary>
        public bool HasChildren
        {
            get { return (ChildRecords.Count > 0); }
        }

        /// <summary>
        /// Gets or sets the Id of the GEDCOMRecord
        /// </summary>
        public string Id
        {
            get { return this.id; }
            set { this.id = value; }
        }

        /// <summary>
        /// Gets or sets the Level (Depth) of the GEDCOMRecord
        /// </summary>
        public int Level
        {
            get { return this.level; }
            set { this.level = value; }
        }

        /// <summary>
        /// Gets or sets the Tag of the GEDCOMRecord
        /// </summary>
        public string Tag
        {
            get { return this.tag; }
            set { this.tag = value; }
        }

        /// <summary>
        /// The TagName (INDI, FAM etc)
        /// </summary>
        public GEDCOMTag TagName
        {
            get { return GEDCOMUtil.GetTag(Tag); }
        }

        /// <summary>
        /// Gets or sets the XRefId of the GEDCOMRecord
        /// </summary>
        public string XRefId
        {
            get { return this.xRefId; }
            set { this.xRefId = value; }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Parse parses the text and creates a GEDCOMRecord object
        /// </summary>
        /// <param name="text">The text to parse</param>
        /// <returns>A flag that indicates whther the record was parsed</returns>
        private bool Parse(string text)
        {
            return GEDCOMUtil.ParseGEDCOM(text, this);
        }

        #endregion

        #region Protected Methods
		
        protected string GetChildData(GEDCOMTag childTag)
        {
            return ChildRecords.GetRecordData(childTag);
        }

        protected string GetChildData(GEDCOMTag childTag, GEDCOMTag grandChildTag)
        {
            string data = "";
            GEDCOMRecord child = ChildRecords.GetLineByTag<GEDCOMRecord>(childTag);

            if (child != null)
                data = child.childRecords.GetRecordData(grandChildTag);
            return data;
        }

        protected string GetChildXRefId(GEDCOMTag childTag)
        {
            return ChildRecords.GetXRefID(childTag);
        }

        protected void SetChildData(GEDCOMTag childTag, string data)
        {
            GEDCOMRecord child = ChildRecords.GetLineByTag<GEDCOMRecord>(childTag);

            if (child == null)
                ChildRecords.Add(new GEDCOMRecord(Level + 1, "", "", childTag.ToString(), data));
            else
                child.Data = data;
        }

        protected void SetChildData(GEDCOMTag childTag, GEDCOMTag grandChildTag, string data)
        {
            GEDCOMRecord child = ChildRecords.GetLineByTag<GEDCOMRecord>(childTag);

            if (child == null)
            {
                child = new GEDCOMRecord(Level + 1, "", "", childTag.ToString(), "");
                ChildRecords.Add(child);
            }

            GEDCOMRecord grandChild = child.ChildRecords.GetLineByTag<GEDCOMRecord>(grandChildTag);

            if (grandChild == null)
                child.ChildRecords.Add(new GEDCOMRecord(Level + 2, "", "", grandChildTag.ToString(), data));
            else
                grandChild.Data = data;
        }

        protected void SetChildXRefId(GEDCOMTag childTag, string xRefId)
        {
            GEDCOMRecord child = ChildRecords.GetLineByTag<GEDCOMRecord>(childTag);

            if (child == null)
                ChildRecords.Add(new GEDCOMRecord(Level + 1, "", xRefId, childTag.ToString(), ""));
            else
                child.xRefId = xRefId;
        }

	    #endregion

        #region Public Methods

        /// <summary>
        /// Append a string to the lineValue field
        /// </summary>
        /// <param name="value">The string to append</param>
        public void AppendData(String data)
        {
            this.data += data;
        }

        /// <summary>
        /// Reset all values.
        /// </summary>
        public void Clear()
        {
            this.Level = 0;
            this.Tag = "";
            this.Data = "";
            this.XRefId = "";
        }

        public int GetId()
        {
            int id;
            if (String.IsNullOrEmpty(Id) || !Int32.TryParse(Id.Substring(2, Id.Length - 3), out id))
                id = -1;
            return id;
        }

        /// <summary>
        /// Returns a spacified Child Record based on the tagName
        /// </summary>
        /// <param name="tagName">The name of the Tag</param>
        /// <returns>A GEDCOMRecord</returns>
        public GEDCOMRecord SelectRecordByTag(GEDCOMTag tagName)
        {
            return ChildRecords.GetLineByTag(tagName);
        }

        /// <summary>
        /// Returns a spacified List of Child Records based on the tagName
        /// </summary>
        /// <param name="tagName">The name of the Tag</param>
        /// <returns>A GEDCOMRecordList</returns>
        public GEDCOMRecordList SelectRecordsByTag(GEDCOMTag tagName)
        {
            return ChildRecords.GetLinesByTag(tagName);
        }

        /// <summary>
        /// Splits the Data field into Child CONT records
        /// </summary>
        public void SplitData()
        {
            string[] data = Data.Split(new [] {'\n'});

            if (data.Length > 1)
            {
                //The original Data field holds the first part
                Data = data[0];

                //Add CONT records for eacdh other part
                for (int i = 1; i < data.Length; i++)
                {
                    ChildRecords.Insert(i-1, new GEDCOMRecord(Level + 1, "", "", "CONT", data[i]));
                }
            }
        }

        /// <summary>
        /// ToString creates a string represenattion of the GEDCOMRecord
        /// </summary>
        /// <returns>a String</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Level:");
            sb.Append(Level);
            sb.Append(" Id:");
            sb.Append(Id);
            sb.Append(" Tag:");
            sb.Append(Tag);
            sb.Append(" XRefId:");
            sb.Append(XRefId);
            sb.Append(" Data:");
            sb.Append(Data);

            sb.Append(ChildRecords.ToString());

            return sb.ToString();
        }

        #endregion
    }    
}
